/*
 * Copyright  2017 NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_common.h"
//#include "board.h"
#include "fsl_clock.h"
#include "fsl_iocon.h"
#include "fsl_reset.h"
#include "fsl_sysctl.h"
#include "fsl_i2s.h"
#include "fsl_i2c.h"
#include "fsl_dma.h"
#include "fsl_i2s_dma.h"

#include "fsl_codec_common.h"
#include "fsl_wm8904.h"

#include <stdio.h>
#define PRINTF printf
/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define BOARD_CODEC_I2C_BASEADDR I2C4
#define BOARD_CODEC_I2C_CLOCK_FREQ 12000000
#define BOARD_CODEC_I2C_INSTANCE 4

#define APP_CODEC_SAMPLE_FREQ_HZ 16000 /* 16khz ²ÉÑùÂÊ. */

#define DEMO_I2C (I2C4)
#define DEMO_I2S_MASTER_CLOCK_FREQUENCY (24576000)
#define DEMO_I2S_TX (I2S7)
#define DEMO_I2S_RX (I2S6)
#define DEMO_DMA (DMA0)
#define DEMO_I2S_TX_CHANNEL (19)
#define DEMO_I2S_RX_CHANNEL (16)
// #define I2S_CLOCK_DIVIDER (CLOCK_GetPll0OutFreq() / 48000 / 16U / 2U)
/* ³ýÒÔ16ÊÇÎªÁËµÃµ½16bÊý, ³ýÒÔ2ÊÇ¿¼ÂÇµ½ÓÐÁ½¸öÉùµÀ, ÓÀÔ¶¶¼ÓÐÁ½¸öÉùµÀ */
#define I2S_CLOCK_DIVIDER (CLOCK_GetPll0OutFreq() / APP_CODEC_SAMPLE_FREQ_HZ / 16U / 2U)

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/* Codec functions.
 * 识别的过程将放在CodecLoopBack_RxCallback()函数中.
 */
static void CodecLoopBack_Setup(void);
static void CodecLoopBack_Start(void);
static void CodecLoopBack_TxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData);
static void CodecLoopBack_RxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData);

/*******************************************************************************
 * Variables
 ******************************************************************************/

/* 256个点执行一次处理, 使用两块内存组装成乒乓buffer */
//#define APP_CODEC_SAMPLE_BUFFER_STEP   256
//#define APP_CODEC_SAMPLE_BUFFER_COUNT  2

/* 这个buffer后续不再使用. */
//volatile uint8_t g_CodecSampleBuf[APP_CODEC_SAMPLE_BUFFER_STEP*APP_CODEC_SAMPLE_BUFFER_COUNT] = {0};

/* 256个点是一个DMA的采样块, 也是FFT的一个处理快.
 * 当前块被填满之后, 前一个块被用于进行数据处理, 再前一个块用于播放.
 */
#define APP_CODEC_AUDIO_BLK_SIZE     256
#define APP_CODEC_AUDIO_BLK_COUNT    3 /* 至少为3 */
volatile uint32_t g_CodecAudioBuff[APP_CODEC_AUDIO_BLK_SIZE*APP_CODEC_AUDIO_BLK_COUNT] = {0}; /* 以4字节分配内存 */
volatile uint32_t g_CodecAudioBlkIdx_Input   = (APP_CODEC_AUDIO_BLK_COUNT-1);
volatile uint32_t g_CodecAudioBlkIdx_Compute = (APP_CODEC_AUDIO_BLK_COUNT-2);
volatile uint32_t g_CodecAudioBlkIdx_Output  = (APP_CODEC_AUDIO_BLK_COUNT-3);


/* dma使用的一系列handler */
static dma_handle_t s_DmaTxHandle;
static dma_handle_t s_DmaRxHandle;
static i2s_dma_handle_t s_TxHandle;
static i2s_dma_handle_t s_RxHandle;
static i2s_transfer_t s_TxTransfer;
static i2s_transfer_t s_RxTransfer;
static i2s_config_t s_TxConfig;
static i2s_config_t s_RxConfig;

void BOARD_Codec_I2C_Init(void)
{
    //BOARD_I2C_Init(BOARD_CODEC_I2C_BASEADDR, BOARD_CODEC_I2C_CLOCK_FREQ);
    i2c_master_config_t i2cConfig = {0};
    I2C_MasterGetDefaultConfig(&i2cConfig);
    I2C_MasterInit(BOARD_CODEC_I2C_BASEADDR, &i2cConfig, BOARD_CODEC_I2C_CLOCK_FREQ);
}

status_t BOARD_Codec_I2C_Send(
    uint8_t deviceAddress, uint32_t subAddress, uint8_t subAddressSize,
    const uint8_t *txBuff, uint8_t txBuffSize)
{
    i2c_master_transfer_t masterXfer;

    /* Prepare transfer structure. */
    masterXfer.slaveAddress = deviceAddress;
    masterXfer.direction = kI2C_Write;
    masterXfer.subaddress = subAddress;
    masterXfer.subaddressSize = subAddressSize;
    masterXfer.data = (uint8_t *)txBuff;
    masterXfer.dataSize = txBuffSize;
    masterXfer.flags = kI2C_TransferDefaultFlag;

    return I2C_MasterTransferBlocking(BOARD_CODEC_I2C_BASEADDR, &masterXfer);
}

status_t BOARD_Codec_I2C_Receive(
    uint8_t deviceAddress, uint32_t subAddress, uint8_t subAddressSize,
    uint8_t *rxBuff, uint8_t rxBuffSize)
{
    i2c_master_transfer_t masterXfer;

    /* Prepare transfer structure. */
    masterXfer.slaveAddress = deviceAddress;
    masterXfer.subaddress = subAddress;
    masterXfer.subaddressSize = subAddressSize;
    masterXfer.data = rxBuff;
    masterXfer.dataSize = rxBuffSize;
    masterXfer.direction = kI2C_Read;
    masterXfer.flags = kI2C_TransferDefaultFlag;

    return I2C_MasterTransferBlocking(BOARD_CODEC_I2C_BASEADDR, &masterXfer);
}

codec_config_t boardCodecConfig =
{
   /* defined in user's hardware config file. */
   .I2C_SendFunc = BOARD_Codec_I2C_Send,
   .I2C_ReceiveFunc = BOARD_Codec_I2C_Receive,
   /* defined in fsl_wm8904.c file. */
   .op.Init = WM8904_Init,
   .op.Deinit = WM8904_Deinit,
   .op.SetFormat = WM8904_SetAudioFormat
};

/* ÅäÖÃSycctrl½ö½öÎªÁË´¦Àí¶à¸öI2S¹²ÓÃÒý½ÅµÄÎÊÌâ. */
void BOARD_InitSysctrl(void)
{
    SYSCTL_Init(SYSCTL);
    /* select signal source for share set */
    SYSCTL_SetShareSignalSrc(SYSCTL, kSYSCTL_ShareSet0, kSYSCTL_SharedCtrlSignalSCK, kSYSCTL_Flexcomm7);
    SYSCTL_SetShareSignalSrc(SYSCTL, kSYSCTL_ShareSet0, kSYSCTL_SharedCtrlSignalWS, kSYSCTL_Flexcomm7);
    /* select share set for special flexcomm signal */
    SYSCTL_SetShareSet(SYSCTL, kSYSCTL_Flexcomm7, kSYSCTL_FlexcommSignalSCK, kSYSCTL_ShareSet0);
    SYSCTL_SetShareSet(SYSCTL, kSYSCTL_Flexcomm7, kSYSCTL_FlexcommSignalWS, kSYSCTL_ShareSet0);
    SYSCTL_SetShareSet(SYSCTL, kSYSCTL_Flexcomm6, kSYSCTL_FlexcommSignalSCK, kSYSCTL_ShareSet0);
    SYSCTL_SetShareSet(SYSCTL, kSYSCTL_Flexcomm6, kSYSCTL_FlexcommSignalWS, kSYSCTL_ShareSet0);
}

void CodecLoopBack_Setup(void)
{
    wm8904_config_t codecConfig;
    codec_handle_t codecHandle;

    CLOCK_EnableClock(kCLOCK_InputMux);
    CLOCK_EnableClock(kCLOCK_Iocon);
    CLOCK_EnableClock(kCLOCK_Gpio0);
    CLOCK_EnableClock(kCLOCK_Gpio1);

    BOARD_InitSysctrl(); /* ×¨Îª½â¾ö¶à¸öI2S¹²ÓÃÍ¬Ò»¸öÊ±ÖÓÏßµÄ³åÍ». */

    CLOCK_AttachClk(kFRO12M_to_FLEXCOMM4);/* I2C clock */

    /* ÅäÖÃPLLÊ±ÖÓÔ´
     * PLL½«ÎªI2SµÄÍ¨ÐÅÒýÇæÌá¹©Ô­Ê¼Ê±ÖÓÔ´, I2SµÄ¼Ä´æÆ÷ÈÔÊ¹ÓÃÏµÍ³Ê±ÖÓ.
     */
#if 0
    PMC->PDRUNCFGCLR0 |= PMC_PDSLEEPCFG0_PDEN_XTAL32M_MASK;       /*!< Ensure XTAL16M is on  */
    PMC->PDRUNCFGCLR0 |= PMC_PDSLEEPCFG0_PDEN_LDOXO32M_MASK;      /*!< Ensure XTAL16M is on  */
    //SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK; /*!< Ensure CLK_IN is on  */
    SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_FREQM_ENA_MASK; /*!< Ensure CLK_IN is on  */
    ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_SYSTEM_CLK_OUT_MASK;
#endif

    PMC->PDRUNCFGCLR0 |= PMC_PDRUNCFG0_PDEN_XTAL32M_MASK;   /*!< Ensure XTAL16M is on  */
    PMC->PDRUNCFGCLR0 |= PMC_PDRUNCFG0_PDEN_LDOXO32M_MASK;  /*!< Ensure XTAL16M is on  */
    SYSCON->CLOCK_CTRL |= SYSCON_CLOCK_CTRL_CLKIN_ENA_MASK; /*!< Ensure CLK_IN is on  */
    ANACTRL->XO32M_CTRL |= ANACTRL_XO32M_CTRL_ENABLE_SYSTEM_CLK_OUT_MASK;



    /*!< Switch PLL0 clock source selector to XTAL16M */
    CLOCK_AttachClk(kEXT_CLK_to_PLL0);

    const pll_setup_t pll0Setup = {
        .pllctrl = SYSCON_PLL0CTRL_CLKEN_MASK | SYSCON_PLL0CTRL_SELI(8U) | SYSCON_PLL0CTRL_SELP(31U),
        .pllndec = SYSCON_PLL0NDEC_NDIV(125U),
        .pllpdec = SYSCON_PLL0PDEC_PDIV(8U),
        .pllsscg = {0x0U, (SYSCON_PLL0SSCG1_MDIV_EXT(3072U) | SYSCON_PLL0SSCG1_SEL_EXT_MASK)},
        .pllRate = 24576000U, /* ±ØÐëÎªÕâ¸öÊý. */
        .flags = PLL_SETUPFLAG_WAITLOCK,
    };
    /*!< Configure PLL to the desired values */
    CLOCK_SetPLL0Freq(&pll0Setup);

    CLOCK_SetClkDiv(kCLOCK_DivPll0Clk, 0U, true);
    CLOCK_SetClkDiv(kCLOCK_DivPll0Clk, 1U, false);

    /* I2S clocks */
    CLOCK_AttachClk(kPLL0_DIV_to_FLEXCOMM6);
    CLOCK_AttachClk(kPLL0_DIV_to_FLEXCOMM7);

    /* Attach PLL clock to MCLK for I2S, no divider */
    CLOCK_AttachClk(kPLL0_to_MCLK);
    SYSCON->MCLKDIV = SYSCON_MCLKDIV_DIV(0U);
    SYSCON->MCLKIO = 1U;

    RESET_PeripheralReset(kFC4_RST_SHIFT_RSTn);  /* reset FLEXCOMM for I2C */
    RESET_PeripheralReset(kDMA0_RST_SHIFT_RSTn); /* reset FLEXCOMM for DMA0 */
    RESET_PeripheralReset(kFC6_RST_SHIFT_RSTn);  /* reset FLEXCOMM for I2S */
    RESET_PeripheralReset(kFC7_RST_SHIFT_RSTn);

    /* reset NVIC for FLEXCOMM6 and FLEXCOMM7 */
    NVIC_ClearPendingIRQ(FLEXCOMM6_IRQn);
    NVIC_ClearPendingIRQ(FLEXCOMM7_IRQn);

    /* Enable interrupts for I2S */
    EnableIRQ(FLEXCOMM6_IRQn);
    EnableIRQ(FLEXCOMM7_IRQn);

    /* ÅäÖÃCodec. */
    //PRINTF("Configure I2C\r\n");
    BOARD_Codec_I2C_Init();
    //PRINTF("Codec i2c ready.\r\n");

    //PRINTF("Configure WM8904 codec\r\n");
    WM8904_GetDefaultConfig(&codecConfig);
    codecConfig.format.sampleRate = kWM8904_SampleRate16kHz; /* 16khz */
    codecConfig.mclk_HZ = DEMO_I2S_MASTER_CLOCK_FREQUENCY;
    boardCodecConfig.codecConfig = (void *)&codecConfig;
    if (CODEC_Init(&codecHandle, &boardCodecConfig) != kStatus_Success)
    {
        PRINTF("WM8904_Init failed!\r\n");
        while (1);
    }
    /* Initial volume kept low for hearing safety. */
    /* Adjust it to your needs, 0x0006 for -51 dB, 0x0039 for 0 dB etc. */
    WM8904_SetVolume(&codecHandle, 0x0030, 0x0030);
    PRINTF("Codec configuration ready.\r\n");

    //PRINTF("Configure I2S\r\n");

    /* setup i2s. */
    I2S_TxGetDefaultConfig(&s_TxConfig);
    s_TxConfig.divider = I2S_CLOCK_DIVIDER;
    I2S_RxGetDefaultConfig(&s_RxConfig);
    I2S_TxInit(DEMO_I2S_TX, &s_TxConfig);
    I2S_RxInit(DEMO_I2S_RX, &s_RxConfig);

    /* setup dma hardware. */
    DMA_Init(DMA0);
    DMA_EnableChannel(DMA0, DEMO_I2S_TX_CHANNEL);
    DMA_EnableChannel(DMA0, DEMO_I2S_RX_CHANNEL);
    DMA_SetChannelPriority(DMA0, DEMO_I2S_TX_CHANNEL, kDMA_ChannelPriority3);
    DMA_SetChannelPriority(DMA0, DEMO_I2S_RX_CHANNEL, kDMA_ChannelPriority2);
    /* create dma handlers with i2s channel number filled. */
    DMA_CreateHandle(&s_DmaTxHandle, DMA0, DEMO_I2S_TX_CHANNEL); /* ´´½¨handler²¢Ìî³äÍ¨µÀºÅ */
    DMA_CreateHandle(&s_DmaRxHandle, DMA0, DEMO_I2S_RX_CHANNEL);

    PRINTF("Codec i2s ready.\r\n");
}


/*
* StartDigitalLoopback
* 注意, 当前版本的i2s dma仅仅支持32数搬运, 也就是说, 即使想要单声道, 也总是会搬运两个声道的数据.
* 实际可以通过配置DMA的搬运带宽为16bit, 仅搬运32位的高16位或低16位, 从而在内存中实现紧凑的16位数排列.
* 这里对缓冲区的使用也是错位操作的, 这样做的目的是减少音频输入DMA, 音频输出DMA和CPU访问内存时产生冲突:
* 走在队列最前面的始终是音频输入DMA, 当音频接收DMA捕获到音频数据后, 直接将刚捕获的数据交给CPU执行处理环节,
* 然后自己将指针移动到下一个缓冲区块中, 在前面的块中填充新捕获到的数据. 音频输出DMA是同输入DMA同步的,
* 但是使用的缓冲区块始终晚于当前的输入DMA两个位置(中间的位置是给处理用的). 这样在任何一个瞬间, 输入和输出DMA的
* 指针同时同步移动, 中间缓冲块的指针由CPU控制, 快速处理.
*/
static void CodecLoopBack_Start(void)
{
    /* 这里s_RxTransfer和s_TxTransfer内存空间就被注册到handler里去了.
     * 注意, 这里使用的是内存, 而不是其中的内容.
     * 所以,在正式开始传数之前, 这块内存还可以临时存点别的数. */
    I2S_RxTransferCreateHandleDMA(DEMO_I2S_RX, &s_RxHandle, &s_DmaRxHandle, CodecLoopBack_RxCallback, (void *)&s_RxTransfer);
    I2S_TxTransferCreateHandleDMA(DEMO_I2S_TX, &s_TxHandle, &s_DmaTxHandle, CodecLoopBack_TxCallback, (void *)&s_TxTransfer);

    /* 先准备一些缓冲区 */

    /* 即将填充的接收buffer. */
    s_RxTransfer.data = (volatile uint8_t *)&g_CodecAudioBuff[g_CodecAudioBlkIdx_Input*APP_CODEC_AUDIO_BLK_SIZE];
    s_RxTransfer.dataSize = APP_CODEC_AUDIO_BLK_SIZE*sizeof(uint32_t); /* 以字节为单位 */
    I2S_RxTransferReceiveDMA(DEMO_I2S_RX, &s_RxHandle, s_RxTransfer);

    /* 下一个填充的接收buffer. */
    g_CodecAudioBlkIdx_Input = (g_CodecAudioBlkIdx_Input+1)%APP_CODEC_AUDIO_BLK_COUNT; /* 移动接收缓冲区指针 */
    s_RxTransfer.data = (volatile uint8_t *)&g_CodecAudioBuff[g_CodecAudioBlkIdx_Input*APP_CODEC_AUDIO_BLK_SIZE];
    s_RxTransfer.dataSize = APP_CODEC_AUDIO_BLK_SIZE*sizeof(uint32_t);
    I2S_RxTransferReceiveDMA(DEMO_I2S_RX, &s_RxHandle, s_RxTransfer);

    /* 即将填充的发送buffer. */
    s_TxTransfer.data = (volatile uint8_t *)&g_CodecAudioBuff[g_CodecAudioBlkIdx_Output*APP_CODEC_AUDIO_BLK_SIZE];
    s_TxTransfer.dataSize = APP_CODEC_AUDIO_BLK_SIZE*sizeof(uint32_t);
    I2S_TxTransferSendDMA(DEMO_I2S_TX, &s_TxHandle, s_TxTransfer);

    /* 紧接着要填充的发送buffer. */
    g_CodecAudioBlkIdx_Output = (g_CodecAudioBlkIdx_Output+1)%APP_CODEC_AUDIO_BLK_COUNT; /* 移动发送缓冲区指针 */
    s_TxTransfer.data = (volatile uint8_t *)&g_CodecAudioBuff[g_CodecAudioBlkIdx_Output*APP_CODEC_AUDIO_BLK_SIZE];
    s_TxTransfer.dataSize = APP_CODEC_AUDIO_BLK_SIZE*sizeof(uint32_t);
    I2S_TxTransferSendDMA(DEMO_I2S_TX, &s_TxHandle, s_TxTransfer);
}

/* transfer中的有效信息只是数据指针和数据量. */

static void CodecLoopBack_TxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData)
{

    i2s_transfer_t *transfer = (i2s_transfer_t *)userData; /* 这个时候userData指针就是指向s_TxTransfer的. */

    g_CodecAudioBlkIdx_Output = (g_CodecAudioBlkIdx_Output+1)%APP_CODEC_AUDIO_BLK_COUNT; /* 移动发送缓冲区指针 */
    transfer->data = (volatile uint8_t *)&g_CodecAudioBuff[g_CodecAudioBlkIdx_Output*APP_CODEC_AUDIO_BLK_SIZE];
    transfer->dataSize = APP_CODEC_AUDIO_BLK_SIZE*sizeof(uint32_t);
    I2S_TxTransferSendDMA(DEMO_I2S_TX, handle, *transfer); /* 补入一个新的发送buffer. */
}


/* 当前版本的i2s dma驱动程序仅能支持32b数据搬运, 未实现16b数据搬运.
 * 此时需要在代码中, 用软件方式将16b有效数据提取出来并紧缩到16b数紧缩的数组中, 从而可以直接传递给PowerQuad进行计算.
 */
uint16_t app_audio_data_16b[APP_CODEC_AUDIO_BLK_SIZE];
#if 0
static uint32_t g_kws_value;
static uint32_t g_kws_nn_time = 0; // 执行测量使用的变量
#endif

__weak void audio_codec_rx_frame_hook(uint16_t *buf, uint32_t len) { }

static void CodecLoopBack_RxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData)
{
    i2s_transfer_t *transfer = (i2s_transfer_t *)userData; /* 此时transfer中保存的是刚接收到的数据信息 */
    uint32_t * audio_data_32b_ptr = (uint32_t *)(transfer->data); /* 这里引用的实际上就是g_CodecAudioBlkIdx_Compute位置的内存 */

    for (uint32_t i = 0u; i < APP_CODEC_AUDIO_BLK_SIZE; i++)
    {
        app_audio_data_16b[i] = (uint16_t)(0xFFFF & (*audio_data_32b_ptr)); /* 仅取左声道的16bit并紧凑排列 */
        //app_audio_data_16b[i] = (uint16_t)((0xFFFF0000 & (*audio_data_32b_ptr)) >> 16); /* 仅取左声道的16bit并紧凑排列 */
        audio_data_32b_ptr++;
    }

#if 0
    // 开始计时
    TimerCount_Start();

    /* 处理音频数据, 进行识别 */
    pq_tcn_preprocess(app_audio_data_16b); /* FFT变换 */
    g_kws_value = pq_tcn_run(); /* TCN推理 */

    // 结束计时
    TimerCount_Stop(g_kws_nn_time);
    g_shmem_words[SHMEM_GUI_NN_TIME_REG_IDX] = g_kws_nn_time;

    if (g_kws_value < 11)
    {
        if (!RBUF32_IsFull(&app_kws_cmd_fifo_handle))
        {
            RBUF32_PutDataIn(&app_kws_cmd_fifo_handle, g_kws_value); /* 识别结果入队. */
        }
    }
#endif
    audio_codec_rx_frame_hook(app_audio_data_16b, APP_CODEC_AUDIO_BLK_SIZE);

    /* 装入下一个缓冲区 */
    g_CodecAudioBlkIdx_Input = (g_CodecAudioBlkIdx_Input+1)%APP_CODEC_AUDIO_BLK_COUNT; /* 移动接收缓冲区指针 */
    transfer->data = (volatile uint8_t *)&g_CodecAudioBuff[g_CodecAudioBlkIdx_Input*APP_CODEC_AUDIO_BLK_SIZE];
    transfer->dataSize = APP_CODEC_AUDIO_BLK_SIZE*sizeof(uint32_t);
    I2S_RxTransferReceiveDMA(DEMO_I2S_RX, handle, *transfer); /* 补入一个新的接收buffer. */
}


void audio_codec_setup_pins(void)
{
    /* Enables the clock for the I/O controller.: Enable Clock. */
    CLOCK_EnableClock(kCLOCK_Iocon);
    uint32_t iocon_pin_config;

    /* FC6_SCK. */
    iocon_pin_config = IOCON_PIO_FUNC(1) | IOCON_PIO_MODE(2) | IOCON_PIO_DIGIMODE_MASK
                     | IOCON_PIO_SLEW_MASK | IOCON_PIO_ASW_MASK;
    IOCON_PinMuxSet(IOCON, 0u, 10u, iocon_pin_config);
    /* FC7_TXD_SCL_MISO_WS */
    iocon_pin_config = IOCON_PIO_FUNC(7) | IOCON_PIO_MODE(2) | IOCON_PIO_DIGIMODE_MASK
                     | IOCON_PIO_SLEW_MASK;
    IOCON_PinMuxSet(IOCON, 0u, 19u, iocon_pin_config);
    /* FC7_RXD_SDA_MOSI_DATA */
    iocon_pin_config = IOCON_PIO_FUNC(7) | IOCON_PIO_MODE(2) | IOCON_PIO_DIGIMODE_MASK
                     | IOCON_PIO_SLEW_MASK;
    IOCON_PinMuxSet(IOCON, 0u, 20u, iocon_pin_config);
    /* FC7_SCK. */
    iocon_pin_config = IOCON_PIO_FUNC(7) | IOCON_PIO_MODE(2) | IOCON_PIO_DIGIMODE_MASK
                     | IOCON_PIO_SLEW_MASK;
    IOCON_PinMuxSet(IOCON, 0u, 21u, iocon_pin_config);
    /* FC6_RXD_SDA_MOSI_DATA */
    iocon_pin_config = IOCON_PIO_FUNC(2) | IOCON_PIO_MODE(2) | IOCON_PIO_DIGIMODE_MASK
                     | IOCON_PIO_SLEW_MASK;
    IOCON_PinMuxSet(IOCON, 1u, 13u, iocon_pin_config);
    /* FC6_TXD_SCL_MISO_WS. */
    iocon_pin_config = IOCON_PIO_FUNC(2) | IOCON_PIO_MODE(2) | IOCON_PIO_DIGIMODE_MASK
                     | IOCON_PIO_SLEW_MASK;
    IOCON_PinMuxSet(IOCON, 1u, 16u, iocon_pin_config);
    /* FC4_TXD_SCL_MISO_WS. */
    iocon_pin_config = IOCON_PIO_FUNC(5) | IOCON_PIO_MODE(2) | IOCON_PIO_DIGIMODE_MASK;
    IOCON_PinMuxSet(IOCON, 1u, 20u, iocon_pin_config);
    /* FC4_RXD_SDA_MOSI_DATA. */
    iocon_pin_config = IOCON_PIO_FUNC(5) | IOCON_PIO_MODE(2) | IOCON_PIO_DIGIMODE_MASK;
    IOCON_PinMuxSet(IOCON, 1u, 21u, iocon_pin_config);
    /* MCLK. */
    iocon_pin_config = IOCON_PIO_FUNC(1) | IOCON_PIO_MODE(0) | IOCON_PIO_DIGIMODE_MASK;
    IOCON_PinMuxSet(IOCON, 1u, 31u, iocon_pin_config);
}


void audio_codec_init(void)
{
    audio_codec_setup_pins();
    CodecLoopBack_Setup();
    CodecLoopBack_Start();
}

#if 0
void audio_codec_start(void)
{
    CodecLoopBack_Start();
}
#endif
/* EOF. */

